/*******************************************************************************
 * Witz - Winboard Chess Engine                                                *
 * WINBOARD.C                                                                  *
 *                                                                             *
 * Implements the Winboard interface.                                          *
 *                                                                             *
 * Please refer to the licence notice in MAIN.C                                *
 *                                                                             *
 * Copywright (C) 2006 Pradu Kannan                                            *
 * http://witz.sf.net/                                                         *
 ******************************************************************************/

#include "build.h"
#include "defs.h"
#include "protos.h"
#include "data.h"

bool Ponder, Post, ICS;

/*xboard()
 *like the main() function for the winboard interface
 */
void xboard()
{
	move m;
	char line[256], commandFlag;
	board testboard;
	printf("\n");
	//read the config file
	readConfig();

	//initalize the engine
	initializeEngine();

	if(dumpFileLocationPointer!=NULL)
	{
		loadState(dumpFileLocationPointer);
		printBoard();
	}

	for(;;)
	{
		if(mainboard.side==compside) //if the computer gets to play
		{
			if(!mainboard.AllPieces)
			{
				printf("Error (Illegal Position): current position");
				goto prompt;
			}
			if(gameEnded())
				goto prompt;
			m=getBestMove((void*)(&depthLimit));	//get the best move
			//and make sure it is legal
			if(m==NULLMOVE)
				goto prompt;
			testboard=mainboard;
			if(mainMoveList.moves[0].score<=-MATE)
			{
				printf("resign\n");
				compside=NOSIDE;
			}
			else if(!makemove(&testboard,m))
			{
				printf("move %s\n",moveToString(m));
				makeGameMove(m);
				if(movesInitial)
				{
					movesLeft--;
					if(movesLeft==0)
						movesLeft=movesInitial;
				}
			}
			else
			{
				//Should not get here
				makeGameTakeback();
				printf("Error (bad engine move): %s\n",moveToString(m));
			}
		}
		if(mainboard.AllPieces && Ponder) ponder(); //ponder if pondering is on
prompt:
		if(openedInConsole)
		     printPrompt();
		if(!fgets(line,256,stdin)) return; //on input error quit
		if(*line=='\n') continue; //ignore a newline command
		commandFlag=evaluvateCommand(line); //evaluvate the command if possible
		switch(commandFlag)
		{
			case MF_NULL:
			case MF_NOMOVE:
			case MF_MOVE:
				continue;
		}

		//print an error if string is neither a recognized command or a move
		printf("Error (unknown command):%s\n",line);
	}
}

/******************************************************************************/
/***************************EVALUVATION OF A COMMAND***************************/
/******************************************************************************/

/*evaluvateCommand()
 *evaluvates the command given
 *
 *@param line - the command
 *
 *@returns RET if the calling function needs to return
 *@returns CONT if the calling function needs to continue
 *@returns GO_ON if the calling function should go on with normal operation
 */
char evaluvateCommand(char* line)
{
	char command[256],*temp1,*temp2;
	int i,j;
	U64 l;
	TTprobe probe;
	move m;
	sscanf(line, "%s", command);

	//WINBOARD COMMANDS--------------------------------------------------------

	if(!strcmp(command, "xboard"))
		return MF_NULL;
	if(!strcmp(command, "new"))
	{
		initializeBoard();
		return MF_NOMOVE;
	}
	if(!strcmp(command, "quit"))
		quit();
	if(!strcmp(command, "force"))
	{
		compside=NOSIDE;
		Ponder=false;
		return MF_NOMOVE;
	}
	if(!strcmp(command, "white"))
	{
		mainboard.side=WHITE;
		compside=BLACK;
		return MF_NOMOVE;
	}
	if(!strcmp(command, "black"))
	{
		mainboard.side=BLACK;
		compside=WHITE;
		return MF_NOMOVE;
	}
	if(!strcmp(command, "random"))
		return MF_NULL;
	if(!strcmp(command, "hard"))
	{
		Ponder=true;
		return MF_NULL;
	}
	if(!strcmp(command, "easy"))
	{
		Ponder=false;
		return MF_NULL;
	}
	if(!strcmp(command, "level"))
	{
		sscanf(line,"level %d %d %d",&movesInitial,&timeLeft,&timeInc);
		movesLeft=movesInitial;
		return MF_NOMOVE;
	}
	if(!strcmp(command, "st"))
	{
		timeLeft=0;
		movesLeft=0;
		depthLimit=INF;
		sscanf(line, "st %d", &timeLimit);
		timeLimit*=1000;
		return MF_MOVE;
	}
	if(!strcmp(command, "sd"))
	{
		timeLimit=0;
		timeLeft=0;
		movesLeft=0;
		sscanf(line, "sd %d", &depthLimit);
		if(depthLimit>MAX_PLY)
		{
			if(openedInConsole)
			     printf("Notice: depth cannot be set to greater than %d ply\n",MAX_PLY);
			depthLimit=MAX_PLY;
		}
		if(openedInConsole)
			printf("Depth limit set to %d\n",depthLimit);
		return MF_MOVE;
	}
	if(!strcmp(command, "thread"))
	{
		int tmp;
		sscanf(line, "thread %d", &tmp);
		threaded = (tmp != 0);
		return MF_MOVE;
	}
	if(!strcmp(command, "sti"))
	{
		sscanf(line, "sti %d", &sti);

		if(openedInConsole)
			printf("Single Threaded Iterations set to %d\n",sti);

		return MF_MOVE;
	}
	if(!strcmp(command, "time"))
	{
		timeLimit=0;
		depthLimit=INF;
		sscanf(line, "time %d", &timeLeft);
		timeLeft*=10;
		return MF_NULL;
	}
	if(!strcmp(command, "otim"))
	{
		return MF_NULL;
	}
	if(!strcmp(command, "go"))
	{
		compside = mainboard.side;
		return MF_MOVE;
	}
	if(!strcmp(command, "hint"))
	{
		TTprobe tempProbe;
		if(probeTT(&mainboard,&tempProbe))
			printf("Hint: %s\n",moveToString(tempProbe.m));
		else
			printf("Hint: %s\n",moveToString(mainMoveList.moves->m));
		return MF_NULL;
	}
	if(!strcmp(command, "undo"))
	{
		if(gamedepth<1)
		{
			printf("Error (command not legal now):%s\n",command);
			return MF_NULL;
		}
		makeGameTakeback();
		return MF_NOMOVE;
	}
	if(!strcmp(command, "."))
		return MF_NULL;
	if(!strcmp(command, "remove"))
	{
          if(gamedepth<2)
		{
			printf("Error (command not legal now):%s\n",command);
			return MF_NULL;
		}
		makeGameTakeback();
		makeGameTakeback();
		return MF_NOMOVE;
	}
	if(!strcmp(command, "post"))
	{
		Post=true;
		return MF_NULL;
	}
	if(!strcmp(command, "nopost"))
	{
		Post=false;
		return MF_NULL;
	}
	if(!strcmp(command, "?"))
		return MF_MOVE;
	if(!strcmp(command,"bk"))
	{
		useBook=!useBook;
		if(useBook)
			printf("\t Witz's book is turned on\n\n");
		else
			printf("\t Witz's book is turned off\n\n");
		return MF_NULL;
	}
	if(!strcmp(command,"rating"))
	{
		//TODO (Pradu#6#): Rating Info;
		return MF_NULL;
	}
	if(!strcmp(command,"computer"))
	{
		//TODO (Pradu#6#): Opponent is a computer
		return MF_NULL;
	}
	if(!strcmp(command,"edit"))
	{
		if(edit())
			nullBoard();
		return MF_NOMOVE;
	}
	//TODO (Pradu#2#): remove comment after implementing analyze
	if(!strcmp(command,"analyze"))
	{
		analyze();
		return MF_NULL;
	}
	if(!strcmp(command,"exit"))
	{
		//do the opposite of analyze
		return MF_NULL;
	}
	//WINBOARD 2 COMMANDS------------------------------------------------------

	if(!strcmp(command,"protover"))
	{
		printWB2compatibility();
		return MF_NULL;
	}
	if(!strcmp(command,"accepted") || !strcmp(command,"rejected"))
		return MF_NULL;
	if(!strcmp(command,"ping"))
	{
		sscanf(line,"ping %d",&i);
		printf("pong %d\n",i);
		return MF_NULL;
	}
	if(!strcmp(command,"playother"))
	{
		compside=mainboard.xside;
		return MF_NOMOVE;
	}
	if(!strcmp(command,"draw"))
	{
		//TODO (Pradu#9#): implement draw acceptance;
		return MF_NULL;
	}
	if(!strcmp(command,"result"))
	{
		//TODO (Pradu#9#): Trigger Learning
		return MF_NOMOVE;
	}
	if(!strcmp(command,"setboard"))
	{
		if(setBoard(line))
			nullBoard();
		return MF_NOMOVE;
	}
	if(!strcmp(command,"ics"))
	{
		sscanf(line,"ics %s",command);
		if(!strncmp("-", command, 1))
			ICS=false;
		else
			ICS=true;
		return MF_NULL;
	}
	if(!strcmp(command,"name"))
	{
		//TODO (Pradu#4#): implement name command
		return MF_NULL;
	}

	//WITZ COMMANDS------------------------------------------------------------

	if(!strcmp(command, "d"))
	{
		printBoard();
		return MF_NULL;
	}
	if(!strcmp(command, "divide"))
	{
		sscanf(line,"divide %d",&i);
		if(i>MAX_PLY)
		{
			printf("Error (depth paramater > 64): %s",line);
			return MF_NULL;
		}
		if(i<2)
		{
			printf("Error (depth paramater < 2 - use legalmoves): %s",line);
			return MF_NULL;
		}
		l=U64EMPTY;
		for(j=0;j<mainMoveList.moveCount;j++)
		{
			makeGameMove(mainMoveList.moves[j].m);
			l+=perft((char)(i-1));
			if(!makeGameTakeback())
			{
				printf("Error (divide done on game move limit):no command");
				nullBoard();
				return MF_NULL;
			}
			temp1=toDecimal(leafNodes);
			printf("%d)\t%s\t%s leaf nodes\n",j+1,moveToString(mainMoveList.moves[j].m),temp1);
			free(temp1);
		}
		temp1=toDecimal(l);
		printf("Total: %s leaf nodes\n",toDecimal(l));
		free(temp1);
		return MF_NULL;
	}
	if(!strcmp(command, "hash"))
	{
		i=probeTT(&mainboard,&probe);
		if(i)
		{
			printf("Hashkey: %X%X\n",((unsigned long int)(mainboard.hashkey>>32)),(unsigned long int)(mainboard.hashkey));
			printf("Depth  : %d\n",probe.depth);
			printf("Score  : %d\n",probe.score);
			printf("Flag   : ");
			switch(probe.flag)
			{
			case ALPHA: printf("Alpha"); break;
			case BETA:  printf("Beta");  break;
			case EXACT: printf("Exact"); break;
			default:    printf("ERROR"); break;
			}
			printf("\n");
			printf("Move   : %s\n",moveToSAN(&mainboard,probe.m));
		}
		else
			printf("Position not found in hash table\n");
		return MF_NULL;
	}
	if(!strcmp(line,"help\n"))
	{
		printHelp();
		return MF_NULL;
	}
	if(!strcmp(command, "legalmoves"))
	{
		for(i=0;i<mainMoveList.moveCount;i++)
			printf("%d)\t%s\t(%s) [%d]\n",i+1,moveToSAN(&mainboard,mainMoveList.moves[i].m),moveToString(mainMoveList.moves[i].m),mainMoveList.moves[i].score);
		return MF_NULL;
	}
	if(!strcmp(command, "line"))
	{
		sscanf(line,"line %s",command);
		if((m=parseLegalMove(command,&mainboard))==NULLMOVE)
			printf("Move %s not found\n", command);
		else
			printVariation(m);
		return MF_NULL;
	}
	if(!strcmp(command, "hashline"))
	{
		sscanf(line,"hashline %s",command);
		if((m=parseLegalMove(command,&mainboard))==NULLMOVE)
			printf("Move %s not found\n", command);
		else
			printHashLine(m);
		return MF_NULL;
	}
	if(!strcmp(command, "perft"))
	{
		sscanf(line,"perft %d",&i);
		if(i>MAX_PLY)
		{
			printf("Error (depth paramater > 64): %s",line);
			return MF_NULL;
		}
		if(i<1)
		{
			printf("Error (depth paramater < 1): %s",line);
			return MF_NULL;
		}
		j=getms();
		perft((char)i);
		j=getms()-j;
		temp1=toDecimal(leafNodes);
		temp2=toDecimal(nodes);
		printf("Nodes :\t%s  leaf nodes (%s positions)\nTime  :\t%d\tmilliseconds\nNPS   :\t%.0f\tpositions/second\n",temp1,temp2,j,1000*((double)(nodes))/j);
		free(temp1);
		free(temp2);
		return MF_NULL;
	}
	if(!strcmp(line,"show c\n"))
	{
		printShowC();
		return MF_NULL;
	}
	if(!strcmp(line,"show w\n"))
	{
		printShowW();
		return MF_NULL;
	}
	if(!strcmp(command,"see"))
	{
		sscanf(line,"see %s",command);
		//check if the input is a move in Coordinate Notation
		if((m=parseLegalMove(command,&mainboard))==NULLMOVE)
			printf("Move %s not found\n", command);
		else
			printf("SEE(%s) = %d\n",moveToSAN(&mainboard,m),SEE(&mainboard,m));
		return MF_NULL;
	}
	if(!strcmp(command,"eval"))
	{
		printEval(&mainboard);
		return MF_NULL;
	}
	if(!strcmp(command,"pst"))
	{
		printPCSQ();
		return MF_NULL;
	}

	//Is it a move?
	if((m=parseLegalMove(line,&mainboard))!=NULLMOVE)
	{
		makeGameMove(m);
		return MF_MOVE;
	}

	//Debugging----------------------------------------------------------------

	#ifdef DEBUG_LOADSTATE
		if(!strcmp(command,"loadstate"))
		{
			sscanf(line,"loadstate %s",command);
			loadState(command);
			return MF_NOMOVE;
		}
	#endif

	//return some random value that isn't an MF flag
	return 123;
}

/******************************************************************************/
/******************************XBOARD FUNCTIONS********************************/
/******************************************************************************/

/************************MOVE SPECIFIC XBOARD FUNCTIONS************************/

/*looksLikeAMove()
 *finds out whether a string looks like a coordinate move
 *
 *@param s - a string that might look like a coordinate move
 *
 *@returns true if line does look like a move
 */
bool looksLikeAMove(const char* s)
{
	if(colPart(*s)==-1 || rowPart(s[1])==-1 || colPart(s[2])==-1 || rowPart(s[3])==-1)
		return false;
	return true;
}

/*charToPiece()
 *converts a piece charecter to a piece
 *
 *@param piece-the charecter piece
 *
 *@returns piece as a number coded piece
 */
int charToPiece(char piece)
{
	switch(piece)
	{
		case 'p':case'P':return P;
		case 'n':case'N':return N;
		case 'b':case'B':return B;
		case 'r':case'R':return R;
		case 'q':case'Q':return Q;
		case 'k':case'K':return K;
		default: return EMPTY;
	}
}

/*coordsToSquare()
 *converts a coordinate to a square
 *
 *@param col-the column coordinate, example 'a'
 *@param row-the row coordinate, example '2'
 *
 *@returns the coordinate as a square, example 8
 */
char coordsToSquare(char col, char row)
{
	return (char)(8*(rowPart(row))+colPart(col));
}

/*colPart()
 *gets the column part of a square from a column coordinate
 *
 *@param col - a column coordinate
 *
 *@returns the column part of a coordinate, otherwise -1
 */
int colPart(char col)
{
	switch(col)
	{
		case 'a':case 'A':return 0;
		case 'b':case 'B':return 1;
		case 'c':case 'C':return 2;
		case 'd':case 'D':return 3;
		case 'e':case 'E':return 4;
		case 'f':case 'F':return 5;
		case 'g':case 'G':return 6;
		case 'h':case 'H':return 7;
		default: return -1;
	}
}

/*rowPart()
 *gets the row part of a square from a row coordinate
 *
 *@param row - a row coordinate
 *
 *@returns the row part of a coordinate, otherwise -1
 */
int rowPart(char row)
{
	if(row<'1' || row>'8')
	     return -1;
	return row-'1';
}

/*moveToString()
 *converts a move to an xboard move string
 *print before making move
 *
 *@param m - the move to be turned into a string
 *
 *@returns m as an xboard string
 */
char* moveToString(move m)
{
	static char str2[6], kingpos;
	if(m==NULLMOVE)
		return "Null";
	if(extractSpecial(m) && extractPiece(m)==K)
	{
		kingpos=(char)LastOne(piecesSide(mainboard,K));
		if(extractSpecial(m)>>1) //if Queenside Castling
		{
			m+=encodeFrom(kingpos)+encodeTo(kingpos-2);
		}
		else
			m+=encodeFrom(kingpos)+encodeTo(kingpos+2);
	}
	sprintf(str2, "%c%c%c%c%c",
		ColCoord(extractFrom(m)),
		RowCoord(extractFrom(m)),
		ColCoord(extractTo(m)),
		RowCoord(extractTo(m)),
		PieceToCharLowercase(extractPromotion(m)));
	return str2;
}

/*moveToSAN()
 *Converts a move to a standard algebreic notation (SAN) move string used
 *for friendly user output.  The string representation printed will be with
 *respect to the board position.
 *
 *@param pos - the board position
 *@param m   - the move to be turned into a string
 *
 *@returns m as a string in SAN
 */
char* moveToSAN(const board* pos, move m)
{
	static char str[7];
	char temp,temp2;
	int to,from,movingpiece,capture,promotingpiece,special;
	U64 temp3;
	str[0]='\0';
	to=extractTo(m);
	from=extractFrom(m);
	movingpiece=extractPiece(m);
	capture=extractCapture(m);
	promotingpiece=extractPromotion(m);
	special=extractSpecial(m);
	temp=PieceToCharUppercase(movingpiece);
	if(m==NULLMOVE)
		return "Nullmove";
	if(temp=='K' && special)
	{
		if((special>>1)) //Queenside
			strncat(str,"O-O-O",5);
		else
		     strncat(str,"O-O",3);   //Kingside
		return str;
	}
	else if(temp=='P')
	{
		if(capture || special)
		{
			temp2=ColCoord(from);
			strncat(str,&temp2,1);
		}
	}
	else
	     strncat(str,&temp,1);
	if(!(temp=='P' || temp=='K'))
	{
		if(temp=='B' && ((temp3=(Bmoves(*pos,to)&piecesSide(*pos,B)))&(temp3-1)))
			strncat(str,moveToSANHelper(pos, temp3,from,to),2);
		else if(temp=='R' && ((temp3=(Rmoves(*pos,to)&piecesSide(*pos,R)))&(temp3-1)))
			strncat(str,moveToSANHelper(pos, temp3,from,to),2);
		else if(temp=='Q' && ((temp3=(Qmoves(*pos,to)&piecesSide(*pos,Q)))&(temp3-1)))
			strncat(str,moveToSANHelper(pos, temp3,from,to),2);
		else if(temp=='N' && ((temp3=(Nmoves(*pos,to)&piecesSide(*pos,N)))&(temp3-1)))
			strncat(str,moveToSANHelper(pos, temp3,from,to),2);
	}
	if(capture || special)
	     strncat(str,"x",1);
	strncat(str,getCoord(to),2);
	if(promotingpiece)
	{
		temp=PieceToCharUppercase(promotingpiece);
		strncat(str,"=",1);
	     strncat(str,&temp,1);
	}
	return str;
}
//temp3 - list of other pieces going to that square
//from  - square that the moving piece is from
char* moveToSANHelper(const board* pos, U64 temp3, char from, char to)
{
	char i;
	static char temp[3];
	bool sameCol=false,sameRow=false;
	int pinnedPieces=popCount(temp3)-1;
	//printf("Initial Pinned Pieces=(%d)\n",pinnedPieces);
	for(;temp3;temp3&=(temp3-1))
	{
		i=FirstOne(temp3);
		/*printf("inLoop(%d)\n",i);
		printBitBoard("temp3",temp3);*/
		if(i!=from)
		{
			if(isDirPinnedToK(pos,i,to))
			{
				pinnedPieces--;
				//printf("Reduction=(%d)\n",pinnedPieces);
				continue;
			}
			if(COL(i)==COL(from))
				sameCol=true;
			else if(ROW(i)==ROW(from))
			     sameRow=true;
		}
		if(sameCol && sameRow)
		     break;
	}
	if(!pinnedPieces)
	{
		*temp='\0';
		return temp;
	}
	if(sameRow && sameCol)
	{
		temp[0]=ColCoord(from);
		temp[1]=RowCoord(from);
		temp[3]='\0';
	}
	else if(sameCol)
	{
	     temp[0]=RowCoord(from);
          temp[1]='\0';
	}
	else
	{
          temp[0]=ColCoord(from);
          temp[1]='\0';
	}
	return temp;
}

/*moveToCBSAN()
 *Converts a move to a standard algebreic notation (SAN) move string used
 *for friendly user output.  The string representation printed will be with
 *respect to the board position. The SAN returned is non-optimal so that
 *Witz can handle ChessBase pgn files.
 *
 *@param pos - the board position
 *@param m   - the move to be turned into a string
 *
 *@returns m as a string in SAN
 */
char* moveToCBSAN(const board* pos, move m)
{
	static char str[7];
	char temp,temp2;
	int to,from,movingpiece,capture,promotingpiece,special;
	U64 temp3;
	str[0]='\0';
	to=extractTo(m);
	from=extractFrom(m);
	movingpiece=extractPiece(m);
	capture=extractCapture(m);
	promotingpiece=extractPromotion(m);
	special=extractSpecial(m);
	temp=PieceToCharUppercase(movingpiece);
	if(m==NULLMOVE)
		return "Nullmove";
	if(temp=='K' && special)
	{
		if((special>>1)) //Queenside
			strncat(str,"O-O-O",5);
		else
		     strncat(str,"O-O",3);   //Kingside
		return str;
	}
	else if(temp=='P')
	{
		if(capture || special)
		{
			temp2=ColCoord(from);
			strncat(str,&temp2,1);
		}
	}
	else
	     strncat(str,&temp,1);
	if(!(temp=='P' || temp=='K'))
	{
		if(temp=='B' && ((temp3=(Bmoves(*pos,to)&piecesSide(*pos,B)))&(temp3-1)))
			strncat(str,moveToCBSANHelper(pos, temp3,from,to),2);
		else if(temp=='R' && ((temp3=(Rmoves(*pos,to)&piecesSide(*pos,R)))&(temp3-1)))
			strncat(str,moveToCBSANHelper(pos, temp3,from,to),2);
		else if(temp=='Q' && ((temp3=(Qmoves(*pos,to)&piecesSide(*pos,Q)))&(temp3-1)))
			strncat(str,moveToSANHelper(pos, temp3,from,to),2);
		else if(temp=='N' && ((temp3=(Nmoves(*pos,to)&piecesSide(*pos,N)))&(temp3-1)))
			strncat(str,moveToCBSANHelper(pos, temp3,from,to),2);
	}
	if(capture || special)
	     strncat(str,"x",1);
	strncat(str,getCoord(to),2);
	if(promotingpiece)
	{
		temp=PieceToCharUppercase(promotingpiece);
		strncat(str,"=",1);
	     strncat(str,&temp,1);
	}
	return str;
}
//temp3 - list of other pieces going to that square
//from  - square that the moving piece is from
char* moveToCBSANHelper(const board* pos, U64 temp3, char from, char to)
{
	char i;
	static char temp[3];
	bool sameCol=false,sameRow=false;
	for(;temp3;temp3&=(temp3-1))
	{
		i=FirstOne(temp3);
		if(i!=from)
		{
			if(COL(i)==COL(from))
				sameCol=true;
			else if(ROW(i)==ROW(from))
			     sameRow=true;
		}
		if(sameCol && sameRow)
		     break;
	}
	if(sameRow && sameCol)
	{
		temp[0]=ColCoord(from);
		temp[1]=RowCoord(from);
		temp[3]='\0';
	}
	else if(sameCol)
	{
	     temp[0]=RowCoord(from);
          temp[1]='\0';
	}
	else
	{
          temp[0]=ColCoord(from);
          temp[1]='\0';
	}
	return temp;
}

/*parseMove()
 *parses a move in coordinate notation
 *
 *@param s - a move in coordinate notation
 *
 *@returns s as a move
 */
move parseMove(const char* s)
{
	int from,to,piece,capture,promote=EMPTY,special=0;
	if(!looksLikeAMove(s))
		return NULLMOVE;
	from=coordsToSquare(*s,s[1]);
	to=coordsToSquare(s[2],s[3]);
	piece=mainboard.PieceTypes[from];
	if(!(piece))
		return NULLMOVE;
	if(piece==K)
	{
		if(from-to==2)
		     return castlingMoves[QUEENSIDE];
		else if(to-from==2)
		     return castlingMoves[KINGSIDE];
	}
	capture=mainboard.PieceTypes[to];
	if(piece==P)
	{
		if(ROW(to)==0 || ROW(to)==7)
		{
			promote=charToPiece(s[4]);
			if(promote==K || promote==EMPTY || promote==P)
			     return NULLMOVE;
		}
		if(toBit[to]&mainboard.EP)
		{
			capture=P;
			special=encodeEP;
		}
	}
	return encodeFrom(from)+encodeTo(to)+encodePiece(piece)+encodeCapture(capture)+encodePromotion(promote)+special;
}

/*parseLegalMove()
 *converts a string to a legal move
 *
 *@param move - the string to be converted to a legal move
 *@param pos  - the board position to check for legalmoves
 *
 *@returns the string converted to a legal move
 *@returns NULLMOVE if the move string isn't a legal move
 *
 *NOTE: parameter l is required for efficiency sake in book generation
 */
move parseLegalMove(const char* moveString, const board* pos)
{
	moveList l;
	move m;
	int i;

	generateLegalMoves(pos,&l);

	//check if the input is a move in Coordinate Notation
	if(looksLikeAMove(moveString))
	{
		m=parseMove(moveString);
		for(i=0;i<l.moveCount && ((l.moves[i].m!=m) || (extractFrom(m)!=extractFrom(l.moves[i].m) || extractTo(m)!=extractTo(l.moves[i].m)));i++);
		if(i==l.moveCount)
			return NULLMOVE;
		return l.moves[i].m;
	}

	//check if the first charecters of the input is a move in either SAN or non-optimal Chessbase SAN
	for(i=0;i<l.moveCount;i++)
	{
		//optimal SAN
		if(!strncmp(moveToSAN(pos, l.moves[i].m), moveString, strlen(moveToSAN(pos, l.moves[i].m))))
		{
			if(!strcmp(moveToSAN(pos,l.moves[i].m),"O-O") && !strncmp(moveString,"O-O-O",5))
				continue;
			return l.moves[i].m;
		}
		//non-optimal Chessbase SAN
		else if(!strncmp(moveToCBSAN(pos, l.moves[i].m), moveString, strlen(moveToCBSAN(pos, l.moves[i].m))))
		{
			if(!strcmp(moveToCBSAN(pos,l.moves[i].m),"O-O") && !strncmp(moveString,"O-O-O",5))
				continue;
			return l.moves[i].m;
		}
	}
	return NULLMOVE;
}

/*ColCoord()
 *calculates the column coordinate (letter) for a square
 *
 *@param x - a square
 *
 *@returns the column coordinate of x
 */
char ColCoord(int x)
{
	return 'a'+((char)COL(x));
}

/*RowCoord()
 *calculates the row coordinate (number) for a square
 *
 *@param x - a square
 *
 *@returns the row coordinate of x
 */
char RowCoord(int x)
{
	return '1'+((char)ROW(x));
}

/*getCoord()
 *a coordinate representation of a square
 *
 *@param x - a square
 *
 *@returns the coordinate representation of the square
 */
char* getCoord(int x)
{
	static char str3[3]={'\0','\0','\0'};
	sprintf(str3,"%c%c",ColCoord(x),RowCoord(x));
	return str3;
}


/*PieceToCharLowercase(int x)
 *calculates the lowercase charecter representation of a piece
 *
 *@param x - x must be a piece number (0-7)
 *
 *@returns the lowercase charecter representation of piece x
 */
char PieceToCharLowercase(int x)
{
	static char tempdata[NUMPIECETYPES]={'\0','p','n','b','r','q','k'};
	return tempdata[x];
}

/*PieceToCharUppercase(int x)
 *calculates the uppercase charecter representation of a piece
 *
 *@param x - x must be a piece number (0-7)
 *
 *@returns the uppercase charecter representation of piece x
 */
char PieceToCharUppercase(int x)
{
	static char tempdata[NUMPIECETYPES]={'\0','P','N','B','R','Q','K'};
	return tempdata[x];
}

/***************************OTHER XBOARD FUNCTIONS*****************************/

/*printBoard()
 *an elegant print of the current board position using extended ASCII graphics
 */
void printBoard()
{
	char h;
	U64 i,k;
	int a;
	if(!mainboard.AllPieces)
		puts("\t  \t     Null Board!");
	else if(mainboard.side==BLACK)
		puts("\t  \t    Black to Move");
	else if(mainboard.side==WHITE)
		puts("\t  \t    White to Move");
	printf("\t  %c",PB_UL);
	for(a=0;a<8;a++)
		printf("%c%c%c%c",PB_H,PB_H,PB_H,PB_HD);
	printf("\b%c\n",PB_UR);
	for(k=toBit[56];k;k>>=8)
	{
		printf("\t%c %c ",'1'+LastOne(k)/8,PB_V);
		for(i=k;i && ((LastOne(i)&0x38)==LastOne(k));i<<=1)
  		{
			if(i&mainboard.PiecesSide[BLACK])
				h=PieceToCharLowercase(mainboard.PieceTypes[LastOne(i)]);
			else if(i&mainboard.PiecesSide[WHITE])
				h=PieceToCharUppercase(mainboard.PieceTypes[LastOne(i)]);
			else
			     h=' ';
			printf("%c %c ",h,PB_V);
		}
		printf("\n");
		if(LastOne(k))
		{
			printf("\t  %c",PB_VR);
			for(a=0;a<8;a++)
				printf("%c%c%c%c",PB_H,PB_H,PB_H,PB_CRS);
			printf("\b%c\n",PB_VL);
		}
	}
	printf("\t  %c",PB_LL);
	for(a=0;a<8;a++)
		printf("%c%c%c%c",PB_H,PB_H,PB_H,PB_HU);
	printf("\b%c\n",PB_LR);
	printf("\t   ",PB_LL);
	for(a=0;a<8;a++)
		printf(" %c  ",'a'+a);
	printf("\n");
}

/*edit()
 *edit command from Winboard 1 to set the board position
 *This is an ad-hoc implementation and it dosen't verify input.
 *I don't expect any human to be using edit to setup a board position anyway. This is just
 *included so that Winboard(1) interfaces will also have the ability to send Witz commands
 *to set the internal board.
 *
 *@returns true if edit created an illegal position
 */
bool edit()
{
	bool boardside=mainboard.side, lastcol=WHITE; //lastcol is the color of the last piece
	char line[256],command[256],lastsq=-1; //lastsq is the last square where a piece was placed
	mainboard.fifty=0;
	mainboard.EP=0ULL;
	mainboard.castling=0xF;
	initialMoveNumber=1;
	for(;;)
	{
		if(openedInConsole)
			printf("\nwitz(edit)>");
		if(!fgets(line,256,stdin)) //on input error quit
		{
			printf("Error (bad input):%s",line);
			nullBoard();
			return true;
		}
		if(*line=='\n') continue; //ignore newline
		sscanf(line, "%s", command);
		if(!strcmp(command,"#"))
		{
			nullBoard();
			mainboard.side=boardside;
			mainboard.castling=0xF;
			continue;
		}
		if(!strcmp(command,"c"))
		{
			lastcol=!lastcol;
			continue;
		}
		if(command[0]=='x')
		{
			lastsq=coordsToSquare((char)command[1],(char)command[2]);
			clearSquare(lastsq);
			continue;
		}
		//I know the folling code for adding pieces isn't exactly the best.
		//Nevertheless, it works fine when input is legal.
		if(command[0]=='P' || command [0]=='p')
		{
			lastsq=coordsToSquare((char)command[1],(char)command[2]);
			addPiece(lastcol,P,lastsq);
			continue;
		}
		if(command[0]=='N' || command [0]=='n')
		{
			lastsq=coordsToSquare((char)command[1],(char)command[2]);
			addPiece(lastcol,N,lastsq);
			continue;
		}
		if(command[0]=='B' || command [0]=='b')
		{
			lastsq=coordsToSquare((char)command[1],(char)command[2]);
			addPiece(lastcol,B,lastsq);
			continue;
		}
		if(command[0]=='R' || command [0]=='r')
		{
			lastsq=coordsToSquare((char)command[1],(char)command[2]);
			addPiece(lastcol,R,lastsq);
			continue;
		}
		if(command[0]=='Q' || command [0]=='q')
		{
			lastsq=coordsToSquare((char)command[1],(char)command[2]);
			addPiece(lastcol,Q,lastsq);
			continue;
		}
		if(command[0]=='K' || command [0]=='k')
		{
			lastsq=coordsToSquare((char)command[1],(char)command[2]);
			addPiece(lastcol,K,lastsq);
			continue;
		}
		if(!strcmp(command,"."))
		{
			inferRestOfBoard();
			if(isIllegalPosition())
			{
				if(openedInConsole)
					printf("Error (Illegal Position)\n");
				return true;
			}
			if(openedInConsole)
				printf("Position Set Sucessfully\n");
			gameEnded();
			return false;
		}
		printf("Error (unknown command): %s\n",line);
	}
}

/*analyze()
 *analyze command from Winboard to analyze a game
 */
void analyze(){}; //TODO (Pradu#2#): implement analyze

/*printWB2compatibility()
 *prints commands to show compatability for the Winboard 2 Protocol
 *it is called when the winboard client gives a protover command
 */
void printWB2compatibility()
{
	printf("feature done=0\n");
	printf("feature ping=1\n");
	printf("feature setboard=1\n");
	printf("feature playother=1\n");
	printf("feature san=0\n");
	printf("feature usermove=0\n");
	printf("feature time=1\n");
	printf("feature draw=1\n");
	printf("feature sigint=1\n");
	printf("feature sigterm=1\n");
	printf("feature reuse=0\n");
	printf("feature analyze=0\n"); //TODO (Pradu#2#): update feature command for analyze
	printf("feature myname=\"Witz %s\"\n",PROGRAM_VERSION);
	printf("feature variants=\"normal\"\n");
	printf("feature colors=0\n");
	printf("feature ics=1\n");
	printf("feature name=1\n");
	printf("feature pause=0\n");
	printf("feature done=1\n");
}

/*printResult()
 *prints the result of the game
 *
 *@param side-the side that won (NOSIDE for draw)
 *@param comment-the comment string to print
 */
void printResult(char side, char* comment)
{
	switch(side)
	{
		case WHITE: printf("1-0 {%s}\n",comment); break;
		case BLACK: printf("0-1 {%s}\n",comment); break;
		default: printf("1/2-1/2 {%s}",comment);
	}
}

/*printHelp()
 *prints the console help for the help command
 */
void printHelp()
{
	printf("Please view the readme file that came with this program for a list of commands.\n");
}


/********************************MISCELLANEOUS*********************************/

/*initializeEngine()
 *initializes the program
 */
void initializeEngine()
{
	generateDatabases();
	initializeBoard();
	newBoard=mainboard;
	Ponder=false;
	Post=false;
	ICS=false;
}

/*toDecimal()
 *converts a 64 bit number to a decimal string
 *Note: MAKE SURE TO FREE THE RETURNED POINTER AFTER USE
 *
 *@param x - a U64 number
 *
 *@returns the 64 bt number as a decimal string
 */
char* toDecimal(U64 x)
{
	int i;
	int j;
	char *temp, temp2;
	temp=(char*)malloc(65*sizeof(char));
	if(!x)
	{
		*temp='0';
		temp[2]='\0';
		return temp;
	}
	for(i=0;i<64 && x;i++, x/=10)
		temp[i]='0'+((char)(x%10));
	for(j=0;j<(i/2);j++)
	{
		temp2=temp[j];
		temp[j]=temp[i-j-1];
		temp[i-j-1]=temp2;
	}
	temp[i]='\0';
	return temp;
}

/*printPV()
 *prints the pricipal variation from the hash table
 */
void printPV()
{
	board tempboard=mainboard;
	board tempboard2;
	int i=0;
	moveList moveChoices;
	TTprobe tt_probe;
	drawEntry drawTable[1024];

	drawTable->fiftyReset=(mainboard.hashkey==0);
	drawTable->hashkey=mainboard.hashkey;

	if(!probeTT(&tempboard,&tt_probe))
	{
		printf("Empty PV");
		return;
	}
	while(tt_probe.m!=NULLMOVE && i<50)
	{
		i++;
		tempboard2=tempboard;
		if(makemove(&tempboard,tt_probe.m))
			break;
		drawTable[i].fiftyReset=(extractPiece(tt_probe.m)==P || extractCapture(tt_probe.m))?true:false;
		drawTable[i].hashkey=tempboard.hashkey;
		if(i!=1) //don't print a space for the first move
			printf(" ");
		printf("%s",moveToSAN(&tempboard2,tt_probe.m));
		if(decidable(&tempboard,drawTable,i,&i))
		{
			if(i==0) printf(" Drw");
			return;
		}
		if(inCheck(tempboard,tempboard.side))
		{
			generateLegalMoves(&tempboard,&moveChoices);
			if(moveChoices.moveCount)  //more than one move
				printf("+");
			else
			{
				printf("# Mate in %d",(i+1)/2);
				return;
			}
		}
		if(!probeTT(&tempboard,&tt_probe))
			break;
	}
}

/*printVariation()
 *prints the pricipal variation of a move from the hash table
 *
 *@param smove - the first move of the line
 */
void printVariation(move m)
{
	board tempboard=mainboard;
	board tempboard2;
	int i=0;
	moveList moveChoices;
	TTprobe tt_probe;
	drawEntry drawTable[1024];

	drawTable->fiftyReset=(mainboard.hashkey==0);
	drawTable->hashkey=mainboard.hashkey;

	tt_probe.m=m;
	while(tt_probe.m!=NULLMOVE && i<50)
	{
		i++;
		tempboard2=tempboard;
		if(makemove(&tempboard,tt_probe.m))
			break;
		drawTable[i].fiftyReset=(extractPiece(tt_probe.m)==P || extractCapture(tt_probe.m))?true:false;
		drawTable[i].hashkey=tempboard.hashkey;
		if(i!=1) //don't print a space for the first move
			printf(" ");
		printf("%s",moveToSAN(&tempboard2,tt_probe.m));
		if(decidable(&tempboard,drawTable,i,&i))
		{
			if(i==0) printf(" Drw");
			return;
		}
		if(inCheck(tempboard,tempboard.side))
		{
			generateLegalMoves(&tempboard,&moveChoices);
			if(moveChoices.moveCount)  //more than one move
				printf("+");
			else
			{
				if(inCheck(tempboard,tempboard.side))
					printf("# Mate in %d",(i+1)/2);
				else
					printf(" Stalemate");
				return;
			}
		}
		if(!probeTT(&tempboard,&tt_probe))
			break;
	}
}

/*printHashLine()
 *prints the pricipal variation of a move from the hash table
 *it also prints hash data for the moves
 *
 *@param smove - the first move of the line
 */
void printHashLine(move m)
{
	board tempboard=mainboard;
	board tempboard2;
	int i=0;
	moveList moveChoices;
	TTprobe tt_probe;
	drawEntry drawTable[1024];

	drawTable->fiftyReset=(mainboard.hashkey==0);
	drawTable->hashkey=mainboard.hashkey;

	tt_probe.m=m;
	while(tt_probe.m!=NULLMOVE && i<50)
	{
		i++;
		tempboard2=tempboard;
		if(makemove(&tempboard,tt_probe.m))
			break;
		drawTable[i].fiftyReset=(extractPiece(tt_probe.m)==P || extractCapture(tt_probe.m))?true:false;
		drawTable[i].hashkey=tempboard.hashkey;
		if(i!=1) //don't print a space for the first move
			printf(" ");
		printf("%s",moveToSAN(&tempboard2,tt_probe.m));

		if(i!=1)
		{
			//Print Statistics (depth)(A-ALHPA, B-BETA, =-EXACT)(score)
			printf("(%d",tt_probe.depth);
			switch(tt_probe.flag)
			{
				case ALPHA: printf("A"); break;
				case BETA: printf("B"); break;
				default: printf("=");
			}
			printf(" %d)",tt_probe.score);
		}

		if(decidable(&tempboard,drawTable,i,&i))
		{
			if(i==0) printf(" Drw");
			return;
		}
		if(inCheck(tempboard,tempboard.side))
		{
			generateLegalMoves(&tempboard,&moveChoices);
			if(moveChoices.moveCount)  //more than one move
				printf("+");
			else
			{
				if(inCheck(tempboard,tempboard.side))
					printf("# Mate in %d",(i+1)/2);
				else
					printf(" Stalemate");
				return;
			}
		}
		if(!probeTT(&tempboard,&tt_probe))
			break;
	}
	printf("\n");
	printEval(&tempboard);
}